use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc,
};

use device::{dfs::params::ParamCreateOptions, DeviceManageImpl};
use interrupt::IntBackendDevice;
use memory::MemoryRegionImpl;
use objectid::ObjectId;
use tracelog::{log_error, log_faltal};

const GICV2M_NUM_SPI_MAX: usize = 128;

const V2M_MSI_TYPER: usize = 0x008;
const V2M_MSI_SETSPI_NS: usize = 0x040;
const V2M_MSI_IIDR: usize = 0xFCC;
const V2M_IIDR0: usize = 0xFD0;
const V2M_IIDR11: usize = 0xFFC;

// ASCII code S
const PRODUCT_ID_SIMINK: usize = 0x53;

/// gicv2 msi(x) 状态
pub struct ARMGICv2mState {
    spi: [Arc<IntBackendDevice>; GICV2M_NUM_SPI_MAX],

    base_spi: Arc<AtomicUsize>,
    num_spi: Arc<AtomicUsize>,

    name: String,
}

impl ARMGICv2mState {
    fn gicv2m_set_irq(&self, irq: usize) {
        self.spi[irq].int_pulse();
    }
}

impl MemoryRegionImpl for ARMGICv2mState {
    fn read(&self, offset: u64, size: usize) -> u64 {
        if size != 4 {
            log_error!("gicv2m_read: bad size {}", size);
            return 0;
        }

        match offset as usize {
            V2M_MSI_TYPER => {
                let mut val = (self.base_spi.load(Ordering::Relaxed) + 32) << 16;
                val |= self.num_spi.load(Ordering::Relaxed);
                return val as u64;
            },
            V2M_MSI_IIDR => {
                // 没有任何有效的实现者, 所以将该字段保留为 0, 并根据规范在 arch 修订中返回 0
                return (PRODUCT_ID_SIMINK << 20) as u64;
            },
            V2M_IIDR0..=V2M_IIDR11 => {
                // 没有实现任何可选的标识寄存器, 并且强制性的 MSI_PIDR2 寄存器读取为 0x0,
                // 因此在这里捕获所有实现定义的寄存器
                return 0;
            },
            _ => {
                log_error!("gicv2m_read: Bad offset 0x{:x}", offset);
            },
        }
        0
    }

    fn write(&self, offset: u64, data: u64, size: usize) {
        if size != 2 && size != 4 {
            log_error!("gicv2m_write: bad size {}", size);
            return;
        }

        match offset as usize {
            V2M_MSI_SETSPI_NS => {
                let spi = (data & 0x3ff) - (self.base_spi.load(Ordering::Relaxed) as u64 + 32);
                if spi < self.num_spi.load(Ordering::Relaxed) as u64 {
                    self.gicv2m_set_irq(spi as usize);
                }
            },
            _ => {
                log_error!("gicv2m_write: Bad offset 0x{:x}", offset);
            },
        }
    }

    fn endianness(&self) -> memory::DeviceEndian {
        memory::DeviceEndian::LittleEndian
    }

    fn region_size(&self) -> usize {
        0x1000
    }
}

impl ObjectId for ARMGICv2mState {
    fn compatible(&self) -> &'static str {
        "gicv2m"
    }

    fn id(&self) -> &str {
        &self.name
    }
}

impl DeviceManageImpl for ARMGICv2mState {
    fn realize(&self) {
        let num_spi = self.num_spi.load(Ordering::Relaxed);
        if num_spi > GICV2M_NUM_SPI_MAX {
            log_faltal!(
                "requested {} SPIs exceeds GICv2m frame maximum {}",
                num_spi,
                GICV2M_NUM_SPI_MAX
            );
            return;
        }

        let base_spi = self.base_spi.load(Ordering::Relaxed);
        if base_spi + 32 > 1020 - num_spi {
            log_faltal!(
                "requested base SPI {}+{} exceeds max. number 1020",
                base_spi + 32,
                num_spi
            );
        }

        // TODO: qemu 上还有 nonbroken = true ...
    }

    fn create_options(&self, path: Arc<device::dfs::inode::DfsInode>) -> device::IfDevResult<()> {
        ParamCreateOptions::new()
            .read_write(true)
            .name("base-spi")
            .param_usize(path.clone(), self.base_spi.clone())?;
        ParamCreateOptions::new()
            .read_write(true)
            .name("num-spi")
            .param_usize(path, self.num_spi.clone())
    }

    fn interactive(&self, path: Arc<device::dfs::inode::DfsInode>) -> device::IfDevResult<()> {
        ParamCreateOptions::new()
            .read(true)
            .name("base-spi")
            .param_usize(path.clone(), self.base_spi.clone())?;
        ParamCreateOptions::new().read(true).name("num-spi").param_usize(path, self.num_spi.clone())
    }
}

/// arm-gicv2m 构建器
pub struct ARMGICv2mStateBuilder {
    spi: Option<[Arc<IntBackendDevice>; GICV2M_NUM_SPI_MAX]>,

    base_spi: usize,
    num_spi: usize,

    name: String,
}

impl ARMGICv2mStateBuilder {
    /// 创建一个构建器
    pub fn builder() -> Self {
        Self { spi: None, num_spi: 64, base_spi: 0, name: String::new() }
    }

    /// 设置 gicv2m spi irq 接口
    pub fn set_spi_irq(mut self, spi_irq: [Arc<IntBackendDevice>; GICV2M_NUM_SPI_MAX]) -> Self {
        self.spi = Some(spi_irq);
        self
    }

    /// 设置 gicv2m `base_spi`
    pub fn set_base_spi(mut self, base_spi: usize) -> Self {
        self.base_spi = base_spi;
        self
    }

    /// 设置 gicv2m `num_spi`
    pub fn set_num_spi(mut self, num_spi: usize) -> Self {
        self.num_spi = num_spi;
        self
    }

    /// 设置 gicv2m 名称
    pub fn set_name(mut self, name: &str) -> Self {
        self.name = String::from(name);
        self
    }

    /// 构建一个 gicv2m
    ///
    /// # Panics
    /// 如果配置不符合预期将会 panic
    pub fn build(self) -> ARMGICv2mState {
        ARMGICv2mState {
            spi: self.spi.unwrap(),
            base_spi: Arc::new(AtomicUsize::new(self.base_spi)),
            num_spi: Arc::new(AtomicUsize::new(self.num_spi)),
            name: self.name,
        }
    }
}
